import java.util.*;

public class Statistics {
	//RF format is in percentage point units, Ex - 2.5% would be 2.5
	//RMF format is in product multiple units, Ex = 2.5% would be 1.025
	
	/*Concerns
	 * daily, weekly, monthy, yearly data... how to annualize? need some sort of modifier in a struct
	 * start with small simple statistics class and build from it to build more complicated constructs
	 */
	
	private static final HashMap<Integer, String> stat_map = new HashMap<Integer, String>(){
		{
			put(new Integer(1), "Annualized Geometric Return");
			put(new Integer(2), "Compound Return");
			put(new Integer(3), "Annualizd Arithmetic Standard Deviation");
			put(new Integer(4), "Maximum Drawdown");
			put(new Integer(5), "Second Maximum Drawdown");
			put(new Integer(6), "Cumulative Up-Capture");
			put(new Integer(7), "Cumulative Down-Capture");
			put(new Integer(8), "Annualized Up-Capture");
			put(new Integer(9), "Annualized Down-Capture");
			put(new Integer(10), "Percent Positive Periods");
			put(new Integer(11), "Percent Negative Periods");
			put(new Integer(12), "Mean Return");
			put(new Integer(13), "Highest Single Return");
			put(new Integer(14), "Lowest Single Return");
			put(new Integer(15), "Mean Gain");
			put(new Integer(16), "Mean Loss");
			put(new Integer(17), "Alpha");
			put(new Integer(18), "Beta");
			put(new Integer(19), "Correlation");
			put(new Integer(20), "R-Squared");
			put(new Integer(21), "Sharpe Ratio");
		}
	};
	
	public static HashMap<Integer, Double> calculate(double[] returns_RMF, StatConfig config){
		double[] returns_RF = new double[returns_RMF.length]; 
		for (int i=0; i<returns_RMF.length; i++){
			returns_RF[i] = returns_RMF[i] - 1;
		}
		HashMap<Integer, Double> stats = new HashMap<Integer, Double>();
		for(int i=0; i<config.ids.length; i++){
			switch (config.ids[i]) {
            case 0: stats.put(new Integer(config.ids[i]), new Double(AnnualizedGeometricReturn(returns))); break;
            case 1: stats.put(new Integer(config.ids[i]), new Double(CompoundReturn(returns))); break;
            case 2: stats.put(new Integer(config.ids[i]), new Double(AnnualizedArithmeticStandardDeviation(returns))); break;
            case 3: stats.put(new Integer(config.ids[i]), new Double(MaxDrawdown(returns))); break;
            case 4: stats.put(new Integer(config.ids[i]), new Double(SecondMaxDrawdown(returns))); break;
            case 5: stats.put(new Integer(config.ids[i]), new Double(CumulativeUpCapture(returns))); break;
            case 6: stats.put(new Integer(config.ids[i]), new Double(CumulativeDownCapture(returns))); break;
            case 7: stats.put(new Integer(config.ids[i]), new Double(AnnualizedUpCapture(returns))); break;
            case 8: stats.put(new Integer(config.ids[i]), new Double(AnnualizedDownCapture(returns))); break;
            case 9: stats.put(new Integer(config.ids[i]), new Double(PercentPositivePeriods(returns))); break;
            case 10: stats.put(new Integer(config.ids[i]), new Double(PercentNegativePeriods(returns))); break;
            case 11: stats.put(new Integer(config.ids[i]), new Double(MeanReturn(returns))); break;
            case 12: stats.put(new Integer(config.ids[i]), new Double(HighestSingleReturn(returns))); break;
            case 13: stats.put(new Integer(config.ids[i]), new Double(LowestSingleReturn(returns))); break;
            case 14: stats.put(new Integer(config.ids[i]), new Double(MeanGain(returns))); break;
            case 15: stats.put(new Integer(config.ids[i]), new Double(MeanLoss(returns))); break;
            case 16: stats.put(new Integer(config.ids[i]), new Double(Alpha(returns))); break;
            case 17: stats.put(new Integer(config.ids[i]), new Double(Beta(returns))); break;
            case 18: stats.put(new Integer(config.ids[i]), new Double(Correlation(returns))); break;
            case 19: stats.put(new Integer(config.ids[i]), new Double(RSquared(returns))); break;
            case 20: stats.put(new Integer(config.ids[i]), new Double(SharpeRatio(returns))); break;
			}
		}
		return stats;
	}
	
	public static void checkInput(double[] input){ //checks for valid input depending on the stats being calculated
		if (input.length < 1){throw new MIMOException("Statistics Input Check: Array length 0 invalid");}
	}
	
	//Basic functions
	public static double CompoundReturn(double[] returns_RMF){
		double product = 1.0;
		for(int i = 0; i< returns_RMF.length; i++){
			product *= returns_RMF[i];
		}
		return product;
	}
	
	public static double GeometricAverage(double[] returns_RMF){
		return CompoundReturn(returns_RMF)^(1/returns_RMF.length);
	}
	
	public static double Max(double[] nums){
		double max = nums[0];
		for (int i=1; i<nums.length; i++){
			if (max < nums[i]){max = nums[i];}
		}
		return max;
	}
	
	public static double Min(double[] nums){
		double min = nums[0];
		for (int i=1; i<nums.length; i++){
			if (min > nums[i]){min = nums[i];}
		}
		return min;
	}
	
	public static double Mean(double[] nums){
		double sum = 0;
		for (int i=0; i<nums.length; i++){
			sum += nums[i];
		}
		return sum/nums.length;
	}
	
	public static double ArithmeticStandardDeviation(double[] returns){
		return 0.0;
	}
	
	public static double PercentPositivePeriods(double[] returns_RF){
		int count = 0; 
		for (int i=0; i<returns_RF.length; i++){
			if (returns_RF[i] >= 1){count++;}
		}
		return count/returns_RF.length;
	}
	
	public static double PercentNegativePeriods(double[] returns_RF){
		return 1 - PercentPositivePeriods(returns_RF);
	}
	
	public static double MeanPositive(double[] returns_RF){
		//filter for positive numbers... put in struct that can convert to double[], then put into Mean method
		return 0.0;
	}
	
	public static double MeanNegative(double[] returns_RF){
		//filter for negative numbers.. put in struct that can convert to double[], then put into Mean method
		return 0.0;
	}
	
	
	//Annualized Methods (can extend to monthly, weekly, daily, etc.)
	public static double AnnualizedGeometricReturn(double[] returns, double period_of_data){ //period is fraction of year
		
		return 0.0;
	}
	
	public static double AnnualizedArithmeticStandardDeviation(double[] returns){
		return 0.0;
	}
	
	
	//drawdowns
	public static double MaxDrawdown(double[] returns){
		return 0.0;
	}
	
	public static double SecondMaxDrawdown(double[] returns){
		return 0.0;
	}
	
	
	//risk vs. return (extend Sortino, Information Ratio, etc.)
	public static double SharpeRatio(double[] returns){
		return 0.0;
	}
	
	
	//compare to benchmark
	public static double CumulativeUpCapture(double[] returns){
		return 0.0;
	}
	
	public static double CumulativeDownCapture(double[] returns){
		return 0.0;
	}
	
	public static double AnnualizedUpCapture(double[] returns){
		return 0.0;
	}
	
	public static double AnnualizedDownCapture(double[] returns){
		return 0.0;
	}
	
	
	

	//Regression related
	public static double Alpha(double[] returns){
		return 0.0;
	}
	
	public static double Alpha(double beta){
		return 0.0;
	}
	
	public static double Beta(double[] returns){
		return 0.0;
	}
	
	public static double Correlation(double[] returns){
		return 0.0;
	}
	
	public static double RSquared(double[] returns){
		return 0.0;
	}
	
	public static double RSquared(double R){
		return 0.0;
	}


}
